﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Red.GameData.Raw
{
    /// <summary>
    /// Container for all know types, classes and enumerations in the data system
    /// </summary>
    public partial class ReflectionData
    {
        /// <summary>
        /// Root absolute path of the reflection system (location on this from where the data was loaded)
        /// </summary>
        public string RootPath { get; private set; }

        /// <summary>
        /// Searchable dictionary of all known types (class + enums + static types like int, float, bool and string)
        /// Can be used to verify that a name is a known and recognizable type
        /// </summary>
        public Dictionary<String, ReflectionType> AllTypes { get; private set; }

        /// <summary>
        /// Searchable dictionary of known classes
        /// </summary>
        public Dictionary<String, ReflectionClass> AllClasses { get; private set; }

        /// <summary>
        /// Searchable dicrtionary of known enumerations
        /// </summary>
        public Dictionary<String, ReflectionEnum> AllEnums { get; private set; }

        /// <summary>
        /// Initialization
        /// </summary>
        internal ReflectionData(string rootPath)
        {
            // remember the root path
            // TODO: we could monitor the files...
            RootPath = rootPath;

            // create type registry and register the "static" types
            AllTypes = new Dictionary<String, ReflectionType>();
            AllTypes["int"] = new ReflectionType(this, "int");
            AllTypes["float"] = new ReflectionType(this, "float");
            AllTypes["bool"] = new ReflectionType(this, "bool");
            AllTypes["string"] = new ReflectionType(this, "string");
            AllTypes["name"] = new ReflectionType(this, "name");
            AllTypes["Vector2"] = new ReflectionType(this, "Vector2");
            AllTypes["Vector3"] = new ReflectionType(this, "Vector3");
            AllTypes["Vector4"] = new ReflectionType(this, "Vector4");
            AllTypes["Color"] = new ReflectionType(this, "Color");
            AllTypes["EulerAngles"] = new ReflectionType(this, "EulerAngles");

            // additional dictionaries
            AllClasses = new Dictionary<String, ReflectionClass>();
            AllEnums = new Dictionary<String, ReflectionEnum>();
        }

        /// <summary>
        /// Find class type definition for given name
        /// </summary>
        /// <param name="name">Name of the class to find. Case sensitive.</param>
        /// <returns>Class or NULL if there was no class with that name</returns>
        public ReflectionClass FindClass(string name)
        {
            ReflectionClass ret = null;
            AllClasses.TryGetValue(name, out ret);
            return ret;
        }

        /// <summary>
        /// Find enumeration type definition for given name
        /// </summary>
        /// <param name="name">Name of the enumeration type to find. Case sensitive.</param>
        /// <returns>Enumeration type or NULL if there was no enumeration type with that name</returns>
        public ReflectionEnum FindEnum(string name)
        {
            ReflectionEnum ret = null;
            AllEnums.TryGetValue(name, out ret);
            return ret;
        }

        /// <summary>
        /// Find type by name, searches the whole type library, including the "static" types (int, float, bool and string)
        /// </summary>
        /// <param name="name">Name of the type to find</param>
        /// <returns></returns>
        public ReflectionType FindType(string name)
        {
            ReflectionType ret = null;
            AllTypes.TryGetValue(name, out ret);
            return ret;
        }

        /// <summary>
        /// Get/Create class definition, used by loading code only
        /// A created class definition has the IsDefined flag set to false
        /// This flag is reset only after the type itself is actually loaded, this way we can do a single pass loading.
        /// </summary>
        /// <param name="name">Name of the class to find/create</param>
        /// <returns>The class definition, this may return NULL if the type is already defined as something else than a class (enum)</returns>
        public ReflectionClass GetClassDef(string name)
        {
            // lookup existing type
            ReflectionType existingType = null;
            AllTypes.TryGetValue(name, out existingType);
            if (existingType != null)
                return (existingType as ReflectionClass);

            // create new class, make sure it's not marked as defined
            var ret = new ReflectionClass(this, name);
            Debug.Assert(ret.IsDefined == false);

            // register in type map
            AllClasses[name] = ret;
            AllTypes[name] = ret;
            return ret;
        }

        /// <summary>
        /// Get/Create enumeration definition, used by loading code only
        /// A created enumeration definition has the IsDefined flag set to false
        /// This flag is reset only after the type itself is actually loaded, this way we can do a single pass loading.
        /// </summary>
        /// <param name="name">Name of the enumeration to find/create</param>
        /// <returns>The enumeration definition, this may return NULL if the type is already defined as something else than an enum (a class probably)</returns>
        public ReflectionEnum GetEnumDef(string name)
        {
            // lookup existing type
            ReflectionType existingType = null;
            AllTypes.TryGetValue(name, out existingType);
            if (existingType != null)
                return (existingType as ReflectionEnum);

            // create new enumeration type, make sure it's not marked as defined
            var ret = new ReflectionEnum(this, name );
            Debug.Assert(ret.IsDefined == false);

            // register in type map
            AllEnums[name] = ret;
            AllTypes[name] = ret;
            return ret;
        }

    }
}
